package com.ran.java_gobang.api;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ran.java_gobang.game.OnlineUserManager;
import com.ran.java_gobang.game.Room;
import com.ran.java_gobang.game.RoomManager;
import com.ran.java_gobang.game.response.GameReadyResponse;
import com.ran.java_gobang.game.response.GameResponse;
import com.ran.java_gobang.mapper.UserMapper;
import com.ran.java_gobang.model.User;
import com.ran.java_gobang.serivce.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: ran
 * Date: 2025-08-30
 * Time: 11:44
 */
@Component
@Slf4j
public class GameAPI extends TextWebSocketHandler {
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RoomManager roomManager;

    @Autowired
    private OnlineUserManager onlineUserManager;

    @Autowired
    private UserService userService;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        GameReadyResponse response = new GameReadyResponse();
        // 1. 获取当前用户的信息(从 HttpSession 中拿到)
        User user = (User) session.getAttributes().get("user");
        // 判断当前用户是否登录
        if (user == null) {
            response.setOk(false);
            response.setReason("用户尚未登录!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 2. 当前用户是否进入房间 (用房间管理器进行查询)
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            // 没找到对应房间, 该玩家没有匹配到
            response.setOk(false);
            response.setReason("该玩家尚没有匹配到!");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }
        // 3. 该玩家是不是多开 (是否在其他页面进入游戏) , 用 onlineUserManager
        if (onlineUserManager.getFromGameHall(user.getUserId()) != null || onlineUserManager.getFromGameRoom(user.getUserId()) != null) {
            // 如果当前以一个账号一个在游戏大厅, 一个在游戏房间, 那么也是多开, 需要处理
            response.setOk(true);
            response.setReason("禁止多开游戏界面!");
            response.setMessage("repeatConnection");
            session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));
            return;
        }

        // 4. 设置玩家上线
        onlineUserManager.enterGameRoom(user.getUserId(), session);

        // 5. 把两个玩家加入到游戏房间中
        // 创建房间与匹配是在 game.hall.html 中完成的
        // 所以匹配到对手后, 经过页面跳转, 来到 game_room.html 才是正式进入房间, 再把玩家添加到room中
        // 页面调准会涉及到各种因素, 防止跳转失败无法正常进行游戏
        // 直行道当前逻辑, 说明页面正常跳转了
        synchronized (room) {
            if (room.getUser1() == null) {
                // 第一个玩家尚未添加到房间, 接下来把 第一个连上 websocket 的该玩家 作为 user1 添加到房间
                room.setUser1(user);
                // 设定第一个连接到房间的玩家为先手
                room.setWhiteUser(user.getUserId());

                log.info("玩家1 :" + user.getUserName() + " 已经添加入房间, 准备就绪!");
                return;
            }
            if (room.getUser2() == null) {
                // 进入到该逻辑, 说明玩家1 已经添加到游戏房间, 现在给 玩家2 也添加到房间
                room.setUser2(user);
                log.info("玩家2 :" + user.getUserName() + " 已经添加入房间, 准备就绪!");

                // 两个玩家都添加到房间后, 通知两个玩家 都准备好了, 准备开始游戏
                // 1) 通知玩家1
                noticeGameReady(room, room.getUser1(), room.getUser2());
                // 2) 通知玩家2
                noticeGameReady(room, room.getUser2(), room.getUser1());
                return;
            }
        }

        // 6. 如果又有玩家尝试连接同一个房间, 那么就报错即可
        // 理论上不存在, 但更加健壮
        response.setOk(false);
        response.setReason("当前房间已满, 你不能加入!");
        session.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));


    }

    private void noticeGameReady(Room room, User thisUser, User thatUser) throws IOException {
        GameReadyResponse response = new GameReadyResponse();
        response.setMessage("gameReady");
        response.setReason("");
        response.setOk(true);
        response.setRoomId(room.getRoomId());
        response.setThisUserId(thisUser.getUserId());
        response.setThatUserId(thatUser.getUserId());
        response.setWhiteUser(room.getWhiteUser());
        // 把当前相应数据传给玩家
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thisUser.getUserId());
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));

    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 1. 先从session 中拿到当前用户对象
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            log.error("该玩家没有登陆, user: {}", user);
            return;
        }
        // 2. 通过玩家 id 获取 room
        Room room = roomManager.getRoomByUserId(user.getUserId());

        // 3. 通过 room 对象来处理具体请求
        room.putChess(message.getPayload());

    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            // 简单处理
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if (session == exitSession) {
            // 为了防止多开情况下, 第二个用户断开连接导致第一个用户也被删除
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.info("当前用户: " + user.getUserName() + " 连接游戏房间异常!");
        noticeThatUserMin(user);
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        User user = (User) session.getAttributes().get("user");
        if (user == null) {
            // 简单处理
            return;
        }
        WebSocketSession exitSession = onlineUserManager.getFromGameRoom(user.getUserId());
        if (session == exitSession) {
            // 为了防止多开情况下, 第二个用户断开连接导致第一个用户也被删除
            onlineUserManager.exitGameRoom(user.getUserId());
        }
        log.info("当前用户: " + user.getUserName() + " 离开游戏房间!");
        noticeThatUserMin(user);
    }

    private void noticeThatUserMin(User user) throws IOException {
        // 1. 根据当前玩家找到房间信息
        Room room = roomManager.getRoomByUserId(user.getUserId());
        if (room == null) {
            // 房间已经释放
            System.out.println("该房间已经释放, 无需通知对手!");
            return;
        }
        // 2. 根据房间找到对手
        User thatUser = (user == room.getUser1()) ? room.getUser2() : room.getUser1();
        // 3. 获取对手的在线状态
        WebSocketSession webSocketSession = onlineUserManager.getFromGameRoom(thatUser.getUserId());
        if (webSocketSession == null) {
            // 对手也掉线了, 不用通知对手了
            System.out.println("对手也掉线了!");
            return;
        }
        // 4. 构造响应, 通知对手, 他是获胜方
        GameResponse response = new GameResponse();
        response.setMessage("putChess");
        response.setUserId(thatUser.getUserId());
        response.setWinner(thatUser.getUserId());
        response.setOnlice(true);
        webSocketSession.sendMessage(new TextMessage(objectMapper.writeValueAsString(response)));

        // 更新玩家信息
        int winUserId = thatUser.getUserId();
        int loseUserId = user.getUserId();
        userService.userWin(winUserId);
        userService.userLose(loseUserId);

        // 6. 释放房间对象
        roomManager.remove(room.getRoomId(),room.getUser1().getUserId(),room.getUser2().getUserId());
    }
}
